package com.hzit.hzb.core.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.hzit.common.exception.Assert;
import com.hzit.common.result.ResponseEnum;
import com.hzit.hzb.core.enums.LendStatusEnum;
import com.hzit.hzb.core.enums.TransTypeEnum;
import com.hzit.hzb.core.hfb.FormHelper;
import com.hzit.hzb.core.hfb.HfbConst;
import com.hzit.hzb.core.hfb.RequestHelper;
import com.hzit.hzb.core.mapper.UserAccountMapper;
import com.hzit.hzb.core.pojo.entity.Lend;
import com.hzit.hzb.core.pojo.entity.LendItem;
import com.hzit.hzb.core.mapper.LendItemMapper;
import com.hzit.hzb.core.pojo.entity.TransFlow;
import com.hzit.hzb.core.pojo.vo.InvestVO;
import com.hzit.hzb.core.service.*;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.hzit.hzb.core.util.LendNoUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * <p>
 * 标的出借记录表 服务实现类
 * </p>
 *
 * @author hbxfwf
 * @since 2024-08-27
 */
@Service
@Slf4j
public class LendItemServiceImpl extends ServiceImpl<LendItemMapper, LendItem> implements LendItemService {

	@Autowired
	private LendService lendService;
	@Autowired
	private UserAccountService userAccountService;
	@Autowired
	private UserBindService userBindService;
	@Autowired
	private TransFlowService transFlowService;
	@Autowired
	private UserAccountMapper userAccountMapper;
	/**
	 * 投资
	 * @param vo
	 * @return
	 */
	@Override
	public String commitInvest(InvestVO vo) {
		/*-------------------------- 第一部分： 对前端传入的参数进行校验 ---------------------------*/
		// 1.1 查询出lend标的信息
		Long lendId = vo.getLendId();
		Lend lend = lendService.getById(lendId);
		BigDecimal investAmount = new BigDecimal(vo.getInvestAmount());     // 用户输入的投资金额
		BigDecimal amount = lend.getAmount();                               // 总标的金额
		BigDecimal oldInvestAmount = lend.getInvestAmount();          // 原来己投的金额
		BigDecimal sum = investAmount.add(oldInvestAmount);
		// 1.2 标的状态是否在募资中
		Assert.isTrue(lend.getStatus().intValue() == LendStatusEnum.INVEST_RUN.getStatus().intValue(),
		ResponseEnum.LEND_INVEST_ERROR);
		// 1.3 标的不能超卖：(已投金额 + 本次投资金额 )>=标的金额（超卖）
		Assert.isTrue(sum.doubleValue() <= amount.doubleValue(), ResponseEnum.LEND_FULL_SCALE_ERROR);
		// 1.4 账户可用余额充足：当前用户的余额 >= 当前用户的投资金额（可以投资）
		// 1.4.1 根据userid查询账户余额
		BigDecimal account = userAccountService.getAccount(vo.getInvestUserId());
		Assert.isTrue(account.doubleValue() >= new BigDecimal(vo.getInvestAmount()).doubleValue(),
				ResponseEnum.NOT_SUFFICIENT_FUNDS_ERROR);


		/*-------------------------- 第二部分： 添加一个标的记录(lend_item) ---------------------------*/
		// 2.1 构造一个LendItem
		LendItem lendItem = new LendItem();
		// 2.2 为lendItem设置各个属性
		lendItem.setInvestAmount(new BigDecimal(vo.getInvestAmount()));     // 设置投资金额
		lendItem.setInvestName(vo.getInvestName());                         // 设置投资人姓名
		lendItem.setInvestTime(LocalDateTime.now());                        // 设置投资时间
		lendItem.setInvestUserId(vo.getInvestUserId());                     // 设置投资用户id
		lendItem.setLendEndDate(lend.getLendEndDate());                     // 标的的结束时间
		lendItem.setLendId(lend.getId());                                   // 设置标的id
		String lendItemNo = LendNoUtils.getLendItemNo();
		lendItem.setLendItemNo(lendItemNo);                // 设置标的项（一条标的记录）的编号
		lendItem.setLendStartDate(lend.getLendStartDate());                 // 设置标的开始时间
		lendItem.setLendYearRate(lend.getLendYearRate());                   // 设置年化率
		lendItem.setStatus(0);      // 默认状态
		lendItem.setRealAmount(new BigDecimal(0));                      // 实际收益
		BigDecimal expectAmount =                                           // 计算预期收益
				userAccountService.getInterestCount(new BigDecimal(vo.getInvestAmount()),
						lend.getLendYearRate(),lend.getPeriod(),lend.getReturnMethod());
		lendItem.setExpectAmount(expectAmount);
		this.save(lendItem);                                                // 保存标的记录

		/*-------------------------- 第三部分： 组织表单数据 ---------------------------*/
		// 3.1 创建表单数据
		Map<String, Object> paramMap = new HashMap<>();
		// 3.2 向map中添加数据
		paramMap.put("agentId",HfbConst.AGENT_ID);                          // 商户id

		String bindCode = userBindService.getUserBind(vo.getInvestUserId());
		String benefitBindCode = userBindService.getUserBind(lend.getUserId());
		paramMap.put("voteBindCode",bindCode);                                      // 投资人绑定的协议号
		paramMap.put("benefitBindCode",benefitBindCode);                            // 借款人绑定的协议号
		paramMap.put("agentProjectCode",lend.getLendNo());                          // 项目编号
		paramMap.put("agentProjectName",lend.getTitle());                           // 项目名称
		// 在资金托管平台上的投资订单的唯一编号，要和lendItemNo保持一致。
		paramMap.put("agentBillNo",lendItemNo);
		paramMap.put("voteAmt",vo.getInvestAmount());                               // 投资金额
		paramMap.put("votePrizeAmt","0");                                           // 投资奖励金额
		paramMap.put("voteFeeAmt","0");                                             // 商户手续费
		paramMap.put("projectAmt",lend.getAmount());                                // 项目总金额（标的总金额）
		paramMap.put("note", "");
		paramMap.put("notifyUrl", HfbConst.INVEST_NOTIFY_URL);                      // 异步回调地址
		paramMap.put("returnUrl", HfbConst.INVEST_RETURN_URL);                      // 同步回调地址
		paramMap.put("timestamp", RequestHelper.getTimestamp());                    // 时间戳
		// 3.3 生成签名字符串
		String sign = RequestHelper.getSign(paramMap);
		paramMap.put("sign",sign);                                                  // 设置签名字符串
		//3.4 最后，生成表单字符串
		return FormHelper.buildForm(HfbConst.INVEST_URL,paramMap);
	}

	/**
	 * 投资异步回调
	 * 功能：
	 *  ① 修改userAccount表中的amount及freeze_amount修改
	 *  ② 修改标的记录表(lendItem)中的状态为己支付（1）
	 *  ③ 修改标的信息中的投资金额和投资人数
	 *  ④ 在trans_flow交易流水表中添加记录
	 */
	@Override
	@Transactional(rollbackFor = Exception.class)
	public String lendItemNotify(Map<String, Object> paramMap) {
		// 0. 首先，进行幂等性判断
		String agentBillNo = paramMap.get("agentBillNo") + "";  // 商户订单号
		TransFlow transFlow = transFlowService.getOne(new QueryWrapper<TransFlow>().eq("trans_no", agentBillNo));
		if(transFlow != null){
			log.info("幂等性成立！");
			return "success";
		}
		// 1. 修改userAccount表中的amount及freeze_amount修改
		BigDecimal voteAmt = new BigDecimal(paramMap.get("voteAmt") + "");
		String voteBindCode = (String) paramMap.get("voteBindCode");
		userAccountMapper.updateUserAccount(voteAmt.multiply(new BigDecimal(-1)),voteBindCode,voteAmt);

		// 2. 修改lendItem表的标的记录状态
		LendItem lendItem = getOne(new QueryWrapper<LendItem>().eq("lend_item_no", agentBillNo));
		lendItem.setStatus(1);      // 代表己支付
		this.updateById(lendItem);

		// 3. 修改标的信息
		Lend lend = lendService.getById(lendItem.getLendId());
		lend.setInvestNum(lend.getInvestNum() + 1);     // 投资人数
		lend.setInvestAmount(lend.getInvestAmount().add(voteAmt));
		// if (lend.getAmount().doubleValue() == lend.getInvestAmount().doubleValue()) {
		// 	lend.setStatus(LendStatusEnum.);
		// }
		lendService.updateById(lend);

		// 4. 添加一个交易流水记录
		// Long userId = lendItem.getInvestUserId();
		transFlow = new TransFlow();
		transFlow.setUserName(lendItem.getInvestName());    // 投资人姓名
		transFlow.setTransType(TransTypeEnum.INVEST_LOCK.getTransType()); // 设置交易类型
		transFlow.setTransTypeName(TransTypeEnum.INVEST_LOCK.getTransTypeName()); // 设置交易名称
		transFlow.setTransNo(agentBillNo);                  // 交易单号
		transFlow.setTransAmount(voteAmt);                  // 交易金额
		transFlow.setUserId(lendItem.getInvestUserId());    // userId
		transFlow.setMemo("投资项目编号：" + lend.getLendNo() + "，项目名称：" + lend.getTitle());
		// 保存交易流水
		transFlowService.save(transFlow);

		return "success";
	}

	/**
	 * 根据标的id和状态（己付款）获取标的记录列表
	 * @param id
	 * @param status
	 * @return
	 */
	@Override
	public List<LendItem> selectByLendId(Long id, int status) {
		return this.list(new QueryWrapper<LendItem>().eq("lend_id",id).eq("status",status));
	}

	/**
	 * 根据标的id查询标的项列表
	 * @param id
	 * @return
	 */
	@Override
	public List<LendItem> fetchLendItemList(Long id) {
		return this.list(new QueryWrapper<LendItem>().eq("lend_id",id));
	}
}
